﻿using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;

namespace TH10_Dat_Tools
{

    /// <summary>
    /// 解包类
    /// </summary>
    public class UnPackFiles
    {
        /// <summary>
        /// 将指定Dat文件解码到目标文件夹下
        /// </summary>
        /// <param name="DatFileName">源Dat文件</param>
        /// <param name="DirPath">目标文件夹</param>
        public static void UnPack(string DatFileName, string DirPath)
        {
            FileStream fs = new FileStream(DatFileName, FileMode.Open, FileAccess.Read);
            BinaryReader br = new BinaryReader(fs);
            byte[] buff;
            TH_Head hd = new TH_Head();//文件头
            int buff_point=0;
            int i = 0;
            int count;
            if (DirPath[DirPath.Length - 1] != '\\')
            {
                DirPath =DirPath+ "\\";
            }
            Key[] keys = new Key[8];//密钥组
            int []k={
                0x1b, 0x37, 0x40,  0x2800,
	            0x51, 0xe9, 0x40,  0x3000,
	            0xc1, 0x51, 0x80,  0x3200,
	            0x03, 0x19, 0x400, 0x7800,
	            0xab, 0xcd, 0x200, 0x2800,
	            0x12, 0x34, 0x80,  0x3200,
	            0x35, 0x97, 0x80,  0x2800,
	            0x99, 0x37, 0x400, 0x2000
            };
            for (i = 0; i < 8; i++)
            {
                keys[i] = new Key();
                keys[i].k1 = (byte)k[4 * i + 0];
                keys[i].k2 = (byte)k[4 * i + 1];
                keys[i].block = k[4 * i + 2];
                keys[i].limit = k[4 * i + 3];
            }
            
            
            //获取文件头
            buff = br.ReadBytes(0x10);
            buff = Tools.TH_UnCrypter(buff, 0x1b, 0x37, 0x10, 0x10);//解密头部数据
            hd.Flag = Encoding.Default.GetString(buff, 0, 4);//TH结构标志
            hd.originalSizeOfEntries = Marshal.ReadInt32(buff, 4) - 123456789;//索引段压缩前大小
            hd.compressedSizeofEntries = Marshal.ReadInt32(buff, 8) - 987654321;//索引段压缩后大小
            hd.numOfEntry = Marshal.ReadInt32(buff, 12) - 135792468;//包内文件数

            //判断文件
            if(hd.Flag!="THA1")
            {
                MessageBox.Show("选择的文件结构不匹配");
                return;
            }

            //申请文件索引空间
            TH_Entry []THFiles = new TH_Entry[hd.numOfEntry];

            //读取尾部索引
            fs.Seek(fs.Length - hd.compressedSizeofEntries, SeekOrigin.Begin);
            buff = br.ReadBytes(hd.compressedSizeofEntries);

            //解密索引
            buff = Tools.TH_UnCrypter(buff, 0x3e, 0x9b, 0x80, buff.Length);


            //解压缩索引
            buff = Tools.TH_UnLzss(buff, hd.originalSizeOfEntries);
            
            //填充文件索引结构
            for ( i = 0, buff_point = 0;i<hd.numOfEntry ;i++ )
            {
                THFiles[i] = new TH_Entry();
                count=0;

                //文件名
                while (buff[buff_point+ count] != 0x00)
                    count++;
                THFiles[i].FileName = Encoding.Default.GetString(buff, buff_point, count);
                count = count + (4 - count % 4);
                buff_point = buff_point + count;
                
                //偏移量
                THFiles[i].offset = Marshal.ReadInt32(buff, buff_point);
                buff_point += 4;
                
                //文件压缩前大小
                THFiles[i].orgSize = Marshal.ReadInt32(buff, buff_point);
                buff_point += 8;
            }

            //计算压缩后的文件大小
            for (i = 0; i < hd.numOfEntry - 1; i++)
            {
                THFiles[i].compSize = THFiles[i + 1].offset - THFiles[i].offset;
            }
            THFiles[hd.numOfEntry - 1].compSize = (int)fs.Length - THFiles[hd.numOfEntry - 1].offset-hd.compressedSizeofEntries;//最后一个文件

            //解密/解压缩具体文件，并写入磁盘
            for (i = 0; i < hd.numOfEntry; i++)
            {
                int keyID =Tools.GetKeyID(THFiles[i].FileName);//计算密钥编号
                
                //读取数据
                fs.Seek(THFiles[i].offset, SeekOrigin.Begin);//设置文件指针
                buff = br.ReadBytes(THFiles[i].compSize);

                //解密
                buff = Tools.TH_UnCrypter(buff, keys[keyID].k1, keys[keyID].k2, keys[keyID].block, keys[keyID].limit);

                //解压缩
                if (THFiles[i].compSize != THFiles[i].orgSize)
                {
                    buff = Tools.TH_UnLzss(buff, THFiles[i].orgSize);
                }

                //写入磁盘
                FileStream fs_tmp = new FileStream(DirPath + THFiles[i].FileName, FileMode.Create, FileAccess.Write);
                BinaryWriter bw = new BinaryWriter(fs_tmp);
                bw.Write(buff);
                bw.Close();
                fs_tmp.Close();
            }
            fs.Close();
        }
    }
}